// Effective C++ 条款37：绝不重新定义继承而来的缺省参数值
// 条款37的核心是："绝不重新定义继承而来的虚函数的缺省参数值"（Never redefine a function's inherited default parameter value）。
// 详细解释
// 为什么不应该重新定义继承而来的默认参数值？
// 这个条款涉及到C++中虚函数调用和默认参数值解析的一个重要区别：
// -虚函数调用是动态绑定的（运行时确定）
// -默认参数值是静态绑定的（编译时确定）早绑定
// 这种不一致性会导致令人困惑的行为。

// 问题示例
class Base
{
public:
    virtual void draw(const std::string &s = "Base") const
    {
        std::cout << "Base drawing " << s << std::endl;
    }
};

class Derived : public Base
{
public:
    virtual void draw(const std::string &s = "Derived") const override
    {
        std::cout << "Derived drawing " << s << std::endl;
    }
};

int main()
{
    Derived d;
    Base *pb = &d;

    pb->draw(); // 调用 Derived::draw("Base")
    d.draw();   // 调用 Derived::draw("Derived")
}
// 原因分析
// 当通过指针或引用调用虚函数时：
// 函数的调用版本取决于对象的动态类型（运行时确定）
// 默认参数值取决于指针或引用的静态类型（编译时确定）
// 这种不一致性违反了直觉，容易导致错误。

// 正确的做法
// 方法1：所有派生类使用相同的默认参数值
class Base
{
public:
    virtual void draw(const std::string &s = "Default") const
    {
        std::cout << "Base drawing " << s << std::endl;
    }
};

class Derived : public Base
{
public:
    virtual void draw(const std::string &s = "Default") const override
    {
        std::cout << "Derived drawing " << s << std::endl;
    }
};
// 但这种方法有代码重复问题，如果需要修改默认值，必须修改所有派生类。

// 方法2：使用非虚函数包装虚函数（NVI模式）
class Base
{
public:
    void draw() const
    {                      // 非虚函数
        doDraw("Default"); // 调用虚函数，传递默认参数
    }

    void draw(const std::string &s) const
    { // 非虚函数重载
        doDraw(s);
    }

protected:
    virtual void doDraw(const std::string &s) const
    { // 虚函数，无默认参数
        std::cout << "Base drawing " << s << std::endl;
    }
};

class Derived : public Base
{
protected:
    virtual void doDraw(const std::string &s) const override
    { // 重写虚函数
        std::cout << "Derived drawing " << s << std::endl;
    }
};

// 这种方法使用非虚接口模式（NVI，Non-Virtual Interface），将默认参数放在非虚函数中，避免了默认参数与虚函数调用的不一致问题。

// 实际应用
// 设计考虑
// 可维护性：使用NVI模式可以集中管理默认参数，减少代码重复
// 一致性：确保无论通过什么类型的指针或引用调用函数，行为都是一致的
// 封装：NVI模式还提供了额外的封装，允许在调用虚函数前后执行通用代码
// 其他替代方案
// 使用函数对象或策略模式：完全避免在虚函数中使用默认参数
// 使用重载：为有默认参数和无默认参数的情况提供不同的函数
// 总结
// 条款37强调了C++中静态类型检查和动态多态之间的一个重要区别。默认参数是静态绑定的，而虚函数调用是动态绑定的，这种不一致性可能导致令人困惑的行为。

// 为了避免这个问题：

// 不要在虚函数中使用默认参数
// 如果需要默认参数，考虑使用NVI模式
// 如果必须在虚函数中使用默认参数，确保所有重写的函数使用相同的默认值
// 这条规则帮助我们创建更加一致、可预测和易于维护的类层次结构。